home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 23
/
Aminet 23 (1998)(GTI - Schatztruhe)[!][Feb 1998].iso
/
Aminet
/
text
/
edit
/
Smartindent.lha
/
Smartindent
/
Source
/
semantics_c.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-12-14
|
20KB
|
1,147 lines
/*(( "Kopf" */
/* -----------------------------------------------------------------------------
$Id: semantics_c.c,v 1.5 1997/07/17 00:24:10 mshopf Exp mshopf $
GoldED API client, ©1995 Matthias Hopf.
Compiled with SasC.
C and C++ semantics parser
------------------------------------------------------------------------------
*/
/*)) */
#include "semantics.h"
/*
* uncomment this if you want recursive comments like: / * / * xxx * / * /
*/
// #define RECURSIVE_COMMENTS
/*(( "Private prototypes" */
int isBlockKeyword (char *);
int isAttrKeyword (char *);
FUNC (DoStd);
FUNC (BLOCK);
FUNC (BLOCK_ONE);
FUNC (CMD_STD);
FUNC (CMD_EQ);
FUNC (ROUND_BRACK);
FUNC (SQUARE_BRACK);
FUNC (COMMENT);
FUNC (CPPCOMMENT);
FUNC (PRAEPROC);
FUNC (STRING_DOUBLE);
FUNC (STRING_SINGLE);
/*)) */
/*(( "CIndent ()" */
/****** Main routine ******/
void Indent_C (sc_t *c)
{
BLOCK (c, 0);
if (c->CurrentLine <= c->EndIndent)
Error (c, UNMATCHED_BRACE_ERROR);
debug (Dbug, ("\n\n")); // Do it always...
}
/*)) */
/*(( "IsWord_C ()" */
int IsWord_C (sc_t *c, char *buf, int len, int maxlen)
{
char *t;
switch (len) {
case 0:
case 1:
return MATCH_TRUE;
case 2:
switch (buf [0]) {
case '/':
switch (buf [1]) {
case '/':
case '*':
return MATCH_EXACT;
}
break;
case '*':
switch (buf [1]) {
case '/':
return MATCH_EXACT;
case '*': /* for / * ... ** ... * / multiline comments */
if (len < maxlen && buf [2] == '/') /* mind the '* /' */
return MATCH_FALSE;
else
return MATCH_EXACT;
}
break;
case '.':
switch (buf [1]) {
case '.':
if (len < maxlen && buf [2] == '.') /* potential ... */
return MATCH_TRUE;
return MATCH_FALSE; /* we have no '..' in C */
}
break;
case '+':
switch (buf [1]) {
case '+':
return MATCH_EXACT;
}
break;
case '-':
switch (buf [1]) {
case '>':
case '-':
return MATCH_EXACT;
}
break;
case ':':
switch (buf [1]) {
case ':':
return MATCH_EXACT;
}
break;
case '&':
switch (buf [1]) {
case '&':
return MATCH_TRUE; /* &&= possible */
}
break;
case '|':
switch (buf [1]) {
case '|':
return MATCH_TRUE; /* ||= possible */
}
break;
case '^':
switch (buf [1]) {
case '^':
return MATCH_TRUE; /* ^^= possible */
}
break;
case '>':
switch (buf [1]) {
case '>':
return MATCH_TRUE; /* >>= possible */
}
break;
case '<':
switch (buf [1]) {
case '<':
return MATCH_TRUE; /* <<= possible */
}
break;
}
switch (buf [1]) {
case '=': /* ==, !=, +=, *=, ...*/
switch (buf [0]) {
case '!':
case '=':
case '>':
case '<':
case '+':
case '-':
case '*':
case '/':
case '%':
case '|':
case '&':
case '^':
return MATCH_EXACT;
}
return MATCH_FALSE;
}
break;
case 3:
switch (buf [2]) {
case '.': /* printf (char *format, ...) */
if (buf [1] == '.' && buf [2] == '.')
return MATCH_EXACT;
return MATCH_FALSE;
case '=': /* >>=, &&=, ...*/
if (buf [0] == buf [1])
switch (buf [0]) {
case '>':
case '<':
case '&':
case '|':
case '^':
return MATCH_EXACT;
}
return MATCH_FALSE;
}
break;
}
if (isalpha (buf [0]) || buf[0] == '_')
{
for (t = buf+1; t < & buf [len]; t++)
if (!isalnum (*t) && *t != '_')
return MATCH_FALSE;
return MATCH_TRUE;
}
if (isdigit (buf [0]) || buf [0] == '.' || buf [0] == '-' || buf [0] == '+')
{
for (t = buf+1; t < & buf [len]; t++)
if (!isdigit (*t) && *t != '.' && *t != 'e')
return MATCH_FALSE;
return MATCH_TRUE;
}
return MATCH_FALSE;
}
/*)) */
/*(( "KeyPress_C ()" */
void KeyPress_C (sc_t *c, int key)
{
int Line = c->Edit->Line;
int Col = c->Edit->Column;
int Len, i;
char *Text;
int StartLine = -1, EndLine = -1;
if (c->Edit->CurrentBuffer)
{
Text = c->Edit->CurrentBuffer;
Len = c->Edit->CurrentLen;
}
else
{
Text = c->Edit->TextNodes [Line] .Text;
Len = c->Edit->TextNodes [Line] .Len;
}
switch (key) {
case '*':
/* only when first char in line or before or after '/' or '*' */
for (i = 0; i < Col; i++)
if (! isspace (Text [i]))
break;
if (i == Col)
{
StartLine = EndLine = Line;
break;
}
/* no break!*/
case '/':
/* only before or or after '/' or '*' */
if (Col != 0)
{
switch (Text [Col-1]) {
case '/':
case '*':
StartLine = EndLine = Line;
}
}
if (Col < Len-1)
{
switch (Text [Col+1]) {
case '/':
case '*':
StartLine = EndLine = Line;
}
}
break;
case ':':
case '{':
case '}':
StartLine = EndLine = Line;
break;
case '\n':
case '\15':
StartLine = Line > 0 ? Line-1 : 0;
EndLine = Line;
}
if (StartLine >= 0)
{
InitIndent (c, StartLine, EndLine, MODE_LINE | MODE_CURSOR);
Indent_C (c);
CleanupIndent (c);
}
}
/*)) */
/*(( "Semantics structure" */
/****** Method structure ******/
struct Semantic C_Sem =
{
"c",
"C and C++ semantics parser",
"V0.1 ©1995 Matthias Hopf",
IsWord_C,
KeyPress_C,
Indent_C,
{ 4, 0, -2, 2, 1, 4, 40 }
} ;
/*)) */
/*(( "isBlockKeyword ()" */
/****** Test if a character string is a block keyword ******/
int isBlockKeyword (char *t)
{
/* 'if' needs special treatment due to 'else' */
/* 'try' needs special treatment due to 'catch' */
return (streq (t, "if") || streq (t, "else") || streq (t, "do")
|| streq (t, "for") || streq (t, "while")
|| streq (t, "switch") || streq (t, "catch"));
}
/*)) */
/*(( "isAttrKeyword ()" */
/****** Test if a character string is a attribute keyword ******/
/* int, char, etc. are not listed here, because they are types! */
int isAttrKeyword (char *t)
{
return ( streq (t, "enum") || streq (t, "struct") || streq (t, "union")
|| streq (t, "auto") || streq (t, "static") || streq (t, "extern")
|| streq (t, "const") || streq (t, "unsigned") || streq (t, "signed")
|| streq (t, "register") || streq (t, "volatile")
|| streq (t, "template") || streq (t, "friend") || streq (t, "class")
|| streq (t, "inline") || streq (t, "virtual") || streq (t, "asm"));
}
/*)) */
/*(( "DoStd ()" */
/****** Handling of standard situations ******/
FUNC (DoStd)
{
debug (D_PARSER, ("DoStd[%s](%ld)\t", W, I));
dcheck;
switch (W[0]) {
case '#':
if (C == 0)
CALL (PRAEPROC, 0);
else
ERROR (SYNTAX_ERROR);
return;
case '(':
UPFIRST (I);
CALL (ROUND_BRACK, N);
INDENT (I); /* Try to indent ')' */
return;
case '[':
UPFIRST (I);
CALL (SQUARE_BRACK, N);
INDENT (I); /* Try to indent ')' */
return;
case '"':
UPFIRST (I);
CALL (STRING_DOUBLE, C+1);
return;
case '\'':
UPFIRST (I);
CALL (STRING_SINGLE, C+1);
return;
case '/':
switch (W[1]) {
case '/':
if (BOL)
{
if (C == 0)
INDENT (0);
else
INDENT (I+CONFIG.Comment_Level);
}
else
INDENT (CONFIG.LineComment_Abs);
CALL (CPPCOMMENT, 0); /* new indentation level is irrelelvant (one line comment) */
return;
case '*':
if (BOL)
{
if (C == 0)
INDENT (0);
else
INDENT (I+CONFIG.Comment_Level);
}
else
INDENT (CONFIG.LineComment_Abs);
CALL (COMMENT, C);
return;
/* no default */
}
/* no break */
default:
ERROR (INTERNAL_ERROR);
return;
}
}
/*)) */
/*(( "BLOCK ()" */
FUNC (BLOCK)
{
debug (D_PARSER, ("BLOCK(%ld)\t", I));
dcheck;
SETBOL; /* Always indent first thing after '{' */
DOGET
{
c->BlockIndent = I;
switch (W[0]) { // no test for length == 1 !
case '(':
case '[':
case '#':
CALL1 (DoStd);
break;
case '{':
INDENT (I + CONFIG.Brace_Level);
CALL (BLOCK, I + CONFIG.Block_Level);
INDENT (I + CONFIG.Brace_Level);
break;
case '}':
return;
case ';':
UPFIRST (I);
break;
case ',':
CALL (CMD_STD, I + CONFIG.Cont_Level);
break;
case '"':
case '\'':
UPFIRST (I);
UNGET;
CALL (CMD_STD, C+1);
break;
case '=': /* struct bla foo = { }; */
if (W[1] == '\0')
{
UPFIRST (I);
CALL (CMD_EQ, N);
break;
}
// no break!!!, e.g. >=
case ')':
case ']':
case ':':
case '?':
ERROR (SYNTAX_ERROR);
case '\\':
if (EOL && W[1] == '\0')
break;
ERROR (SYNTAX_ERROR);
case '/': // Comment
if (W[1] != 0)
{
CALL1 (DoStd);
break;
}
// no break!!!
default:
UPFIRST (I);
if (streq (W, "else"))
{
PEEK;
if (streq (P, "if"))
{
GET;
INDENT (I);
}
CALL (BLOCK_ONE, I + CONFIG.Block_Level);
}
else if (isBlockKeyword (W)) // else is already handled
CALL (BLOCK_ONE, I + CONFIG.Block_Level);
else if (! isAttrKeyword (W))
{
if (I == 0) /* First level indentation will stay 0 (function definitions) */
CALL (CMD_STD, 0);
else
{
if (EOL)
CALL (CMD_STD, I + CONFIG.Cont_Level);
else
CALL (CMD_STD, N);
}
PEEK; /* have we encountered a label? (special case!) */
if (P[0] == ':' && P[1] == '\0')
{
/* the current line is indented as a label if, and only if
* a) it was only preceeded by *one* alphanummeric word
* b) a 'case' is found as the first word in the line.
* public:, private:, etc. are handled by case a).
*/
char *Text;
int Len, i;
GetText (c, &Text, &Len);/* get first word in line */
for (i = 0; i <= Len; i++)
if (! isspace (Text[i]))
break;
if (i+5 <= Len) /* check for 'case' */
{
if (strncmp (&Text[i], "case", 4) == 0)
{
if (isspace (Text[i+4]))
i = -1;
}
}
if (i != -1)
{ /* else check whether ':' comes immedeately after the first word */
for ( ; i < Len; i++)
if (! isalnum (Text[i]))
break;
for ( ; i < Len; i++)
if (! isspace (Text[i]))
break;
if (Text[i] == ':')
i = -1;
}
if (i == -1)
{ /* Ok, we *really* got a label */
GET;
GetText (c, &Text, &Len);
for (i = 0; i <= C; i++)
if (! isspace (Text [i]))
break;
Move (c, i, c->BlockIndent + CONFIG.Label_Level);
I = c->BlockIndent;
break;
}
GET; /* otherwise this was some C++ statement */
}
}
}
}
}
/*)) */
/*(( "BLOCK_ONE ()" */
FUNC (BLOCK_ONE)
{
int Initial;
debug (D_PARSER, ("BLOCK_ONE(%ld)\t", I));
dcheck;
Initial = I - CONFIG.Block_Level;
SETBOL; /* Always indent first thing after '{' */
DOGET
{
switch (W[0]) { // no test for length == 1 !
case '(':
case '[':
case '#':
CALL1 (DoStd);
break;
case '{':
INDENT (Initial + CONFIG.Brace_Level);
CALL (BLOCK, Initial + CONFIG.Block_Level);
INDENT (Initial + CONFIG.Brace_Level);
return;
case ';':
UPFIRST (I);
return;
case ',':
CALL (CMD_STD, I + CONFIG.Cont_Level);
break;
case '}':
case '=': // incl. ==
case ')':
case ']':
case ':':
case '?':
case '"':
case '\'':
ERROR (SYNTAX_ERROR);
case '\\':
if (EOL && W[1] == '\0')
break;
ERROR (SYNTAX_ERROR);
case '/': // Comment
if (W[1] != 0)
{
CALL1 (DoStd);
break;
}
// no break!!!
default:
UPFIRST (I);
if (streq (W, "if"))
{
for (;;)
{
CALL (BLOCK_ONE, I + CONFIG.Block_Level);
PEEK;
if (streq (P, "else"))
{
GET;
UPFIRST (I);
PEEK;
if (streq (P, "if"))
{
GET;
INDENT (I);
continue;
}
CALL (BLOCK_ONE, I + CONFIG.Block_Level);
}
return;
}
}
else if (isBlockKeyword (W)) // if is already handled
CALL (BLOCK_ONE, I + CONFIG.Block_Level);
else if (! isAttrKeyword (W))
{
if (I == 0) /* First level indentation will stay 0 (function definitions) */
CALL (CMD_STD, 0);
else
{
if (EOL)
CALL (CMD_STD, I + CONFIG.Cont_Level);
else
CALL (CMD_STD, N);
}
}
}
}
}
/*)) */
/*(( "BLOCK_SET ()" */
FUNC (BLOCK_SET)
{
debug (D_PARSER, ("BLOCK_SET(%ld)\t", I));
dcheck;
if (EOL) /* Only indent first thing after '{' when we are in a new line */
SETBOL;
else
I = N; /* Otherwise we take the next char position as intendation */
DOGET
{
c->BlockIndent = I;
switch (W[0]) { // no test for length == 1 !
case '(':
case '[':
case '#':
CALL1 (DoStd);
break;
case '{':
INDENT (I + CONFIG.Brace_Level);
CALL (BLOCK_SET, I + CONFIG.Block_Level);
INDENT (I + CONFIG.Brace_Level);
break;
case '}':
return;
case ',':
UPFIRST (I);
break;
case '"':
case '\'':
UPFIRST (I);
UNGET;
CALL (CMD_STD, N);
break;
case ';':
case '=': // incl. ==
case ')':
case ']':
case ':':
case '?':
ERROR (SYNTAX_ERROR);
case '\\':
if (EOL && W[1] == '\0')
break;
ERROR (SYNTAX_ERROR);
case '/': // Comment
if (W[1] != 0)
{
CALL1 (DoStd);
break;
}
// no break!!!
default:
UPFIRST (I);
if (isBlockKeyword (W))
ERROR (SUSPICIOUS_ERROR);
else if (! isAttrKeyword (W))
{
if (EOL)
CALL (CMD_STD, I + CONFIG.Cont_Level);
else
CALL (CMD_STD, N);
}
}
}
}
/*)) */
/*(( "ROUND_BRACK ()" */
FUNC (ROUND_BRACK)
{
debug (D_PARSER, ("ROUND_BRACK(%ld)\t", I));
dcheck;
DOGET
{
switch (W[0]) { // no test for length == 1 !
case '(':
case '[':
case '#':
case '\'':
case '"':
CALL1 (DoStd);
break;
case ')':
return; /* Indented by DoStd */
case ';': /* for (;;) */
UPFIRST (I);
break;
case '{':
case '}':
case ']':
ERROR (SYNTAX_ERROR);
case '\\':
if (W[1] == 0)
break;
ERROR (SYNTAX_ERROR);
case '/': // Comment
if (W[1] != 0)
{
CALL1 (DoStd);
break;
}
// no break!!!
default:
UPFIRST (I);
if (isBlockKeyword (W))
ERROR (SUSPICIOUS_ERROR);
else
CALL1 (CMD_STD);
}
}
}
/*)) */
/*(( "SQUARE_BRACK ()" */
FUNC (SQUARE_BRACK)
{
debug (D_PARSER, ("SQUARE_BRACK(%ld)\t", I));
dcheck;
DOGET
{
switch (W[0]) { // no test for length == 1 !
case '(':
case '[':
case '#':
case '\'':
case '"':
CALL1 (DoStd);
break;
case ']':
return; /* Indented by DoStd */
case ';': /* for (;;) */
case '{':
case '}':
case ')':
case '=':
case ':':
case '?':
ERROR (SYNTAX_ERROR);
case '\\':
if (W[1] == 0)
break;
ERROR (SYNTAX_ERROR);
case '/': // Comment
if (W[1] != 0)
{
CALL1 (DoStd);
break;
}
// no break!!!
default:
UPFIRST (I);
if (isBlockKeyword (W))
ERROR (SUSPICIOUS_ERROR);
else
CALL1 (CMD_STD);
}
}
}
/*)) */
/*(( "CMD_STD ()" */
FUNC (CMD_STD)
{
char real [4];
debug (D_PARSER, ("CMD_STD(%ld)\t", I));
dcheck;
DOGET
{
strncpy (real, W, 3);
if (W[1] == '=' || (W[1] != '\0' && W[2] == '='))
W[0] = '=';
switch (W[0]) { // no test for length == 1 !
case '(':
case '[':
case '"':
case '\'':
case '#':
CALL1 (DoStd);
break;
case '}':
case ',':
case ';':
case ')':
case ']':
UNGET;
return;
case '=': // = / == / += etc.
if (real[1] == '=' && real [2] == '\0')
{
if (real[0] == '=' || real[0] == '<' || real [0] == '>' || real [0] == '!')
{
UPFIRST (I);
break;
}
}
// no break!!!
case '?':
UPFIRST (I);
CALL (CMD_EQ, N);
break;;
case ':': // : / ::
if (W[1] == '\0')
{
UNGET;
return;
}
UPFIRST (I);
break;
case '{':
I = c->BlockIndent;
INDENT (I + CONFIG.Brace_Level);
CALL (BLOCK, I + CONFIG.Block_Level);
INDENT (I + CONFIG.Brace_Level);
return;
case '\\':
if (W[1] == 0)
break;
ERROR (SYNTAX_ERROR);
case '/': // Comment
if (W[1] != 0)
{
CALL1 (DoStd);
break;
}
// no break!!!
default:
UPFIRST (I);
if (isBlockKeyword (W))
ERROR (SUSPICIOUS_ERROR);
}
}
}
/*)) */
/*(( "CMD_EQ ()" */
FUNC (CMD_EQ)
{
debug (D_PARSER, ("CMD_EQ(%ld)\t", I));
dcheck;
DOGET
{
switch (W[0]) { // no test for length == 1 !
case '(':
case '[':
case '"':
case '\'':
case '#':
CALL1 (DoStd);
break;
case '}':
case ',':
case ';':
case ')':
case ']':
UNGET;
return;
case '?':
UPFIRST (I);
CALL (CMD_EQ, N);
break;;
case '{':
I = c->BlockIndent;
INDENT (I + CONFIG.Brace_Level);
CALL (BLOCK_SET, I + CONFIG.Block_Level);
INDENT (I + CONFIG.Brace_Level);
return;
case '\\':
if (W[1] == 0)
break;
ERROR (SYNTAX_ERROR);
case '/': // Comment
if (W[1] != 0)
{
CALL1 (DoStd);
break;
}
// no break!!!
default:
UPFIRST (I);
if (isBlockKeyword (W))
ERROR (SUSPICIOUS_ERROR);
}
}
}
/*)) */
/*(( "COMMENT ()" */
FUNC (COMMENT)
{
int Intro, Extro;
debug (D_PARSER, ("COMMENT(%ld)\t", I));
dcheck;
Intro = I; /* Position of the '/ *' */
Extro = I; /* Future Position of the '* /' */
I = N;
DOGET
{
if (streq (W, "*/"))
{
UPDATE (Extro);
return;
}
#ifdef RECURSIVE_COMMENTS
else if (streq (W, "/*")) /* We have recursive comments */
CALL1 (DoStd);
#endif
else if (BOL)
{
if (W[0] == '*' && W[1] == '\0')
{
Extro = Intro + 1;
UPDATE (Intro + 1);
}
else if (streq (W, "**"))
{
Extro = Intro;
UPDATE (Intro);
}
else
{
Extro = Intro;
UPDATE (I);
}
}
}
}
/*)) */
/*(( "CPPCOMMENT ()" */
FUNC (CPPCOMMENT)
{
debug (D_PARSER, ("CPPCOMMENT(%ld)\t", I));
dcheck;
GET;
INDENT (I + 3);
UNGET;
DOGET
{
if (EOL)
return;
}
}
/*)) */
/*(( "PRAEPROC ()" */
FUNC (PRAEPROC)
{
char *Text;
int Len, i;
debug (D_PARSER, ("PRAEPROC(%ld)\t", I));
dcheck;
if (EOL)
ERROR (PRAEPROCESSOR_ERROR);
GET; /* get preproc directive (e.g. define) */
if (EOL)
return;
/* get name including parameters */
GetText (c, &Text, &Len);
for (i = N; i < Len; i++)
if (isspace (Text[i]) || Text[i] == '\\')
break;
for ( ; i < Len; i++)
if (! isspace (Text[i]))
break;
C = i;
if (Text[i] == '\\')
SET (I + CONFIG.Cont_Level);
else
SET (i);
DOGET
{
UPFIRST (I);
if (! streq (W, "\\") && EOL)
return;
}
}
/*)) */
/*(( "STRING_SINGLE ()" */
FUNC (STRING_SINGLE)
{
debug (D_PARSER, ("STRING_SINGLE(%ld)\t", I));
dcheck;
if (EOL)
ERROR (STRING_TERMINATION_ERROR);
DOGET
{
if (W[1] == 0)
{
switch (W[0]) {
case '\'':
return;
case '\\':
GET; /* skip next char (may be ' or \ ) */
}
}
if (EOL)
ERROR (STRING_TERMINATION_ERROR);
}
}
/*)) */
/*(( "STRING_DOUBLE ()" */
FUNC (STRING_DOUBLE)
{
debug (D_PARSER, ("STRING_DOUBLE(%ld)\t", I));
dcheck;
if (EOL)
ERROR (STRING_TERMINATION_ERROR);
DOGET
{
if (W[1] == 0)
{
switch (W[0]) {
case '"':
return;
case '\\':
GET; /* skip next char (may be " or \ ) */
}
}
if (EOL)
ERROR (STRING_TERMINATION_ERROR);
}
}
/*)) */